package vip.ola.service.impl.code;

import com.alibaba.fastjson.JSONObject;
import com.baomidou.mybatisplus.core.conditions.query.LambdaQueryWrapper;
import com.baomidou.mybatisplus.core.conditions.update.LambdaUpdateWrapper;
import com.baomidou.mybatisplus.core.metadata.IPage;
import com.baomidou.mybatisplus.extension.plugins.pagination.Page;
import com.baomidou.mybatisplus.extension.service.impl.ServiceImpl;
import com.baomidou.mybatisplus.extension.toolkit.SqlHelper;
import javafx.util.Pair;
import org.apache.commons.collections.CollectionUtils;
import org.apache.commons.collections4.MapUtils;
import org.apache.commons.lang3.StringUtils;
import org.apache.dubbo.config.annotation.Service;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.beans.factory.annotation.Value;
import org.springframework.transaction.annotation.Transactional;
import vip.ola.core.common.Exception.ServiceException;
import vip.ola.core.common.constant.PayConstant;
import vip.ola.core.common.util.MyLog;
import vip.ola.core.common.util.PayUtil;
import vip.ola.core.common.util.RedisUtil;
import vip.ola.core.common.util.WeightRandom;
import vip.ola.core.common.vo.OrderCostFeeVO;
import vip.ola.core.common.vo.UserDayProfitVO;
import vip.ola.core.entity.PayOrder;
import vip.ola.core.entity.code.CodeInfo;
import vip.ola.core.entity.code.CodeOrder;
import vip.ola.core.entity.code.CodeUser;
import vip.ola.core.service.IMchAccountService;
import vip.ola.core.service.IPayOrderService;
import vip.ola.core.service.ISysConfigService;
import vip.ola.core.service.code.CodeAccountHistoryService;
import vip.ola.core.service.code.CodeInfoService;
import vip.ola.core.service.code.CodeOrderService;
import vip.ola.core.service.code.CodeUserService;
import vip.ola.service.dao.mapper.code.CodeOrderMapper;

import java.math.BigDecimal;
import java.time.LocalDate;
import java.time.LocalDateTime;
import java.util.*;
import java.util.concurrent.CopyOnWriteArrayList;
import java.util.function.Function;
import java.util.stream.Collectors;

import static java.math.BigDecimal.ROUND_DOWN;
import static java.math.BigDecimal.ZERO;
import static vip.ola.core.common.constant.CacheKey.*;
import static vip.ola.core.common.constant.CodeConstant.*;
import static vip.ola.core.common.constant.Constant.*;
import static vip.ola.core.common.constant.PayConstant.RETURN_VALUE_SUCCESS;
import static vip.ola.core.common.constant.PayEnum.QRCODE_DISTRIBUTE_ERROR;
import static vip.ola.core.common.constant.PayEnum.QRCODE_NOT_FIND;
import static vip.ola.core.common.constant.RetEnum.*;
import static vip.ola.core.common.constant.SysConfigConstant.*;
import static vip.ola.core.common.util.LocalDateTimeUtil.DATETIME_FORMATTER;

/**
 * <p>
 * 二维码订单信息 服务实现类
 * </p>
 *
 * @author faker
 * @since 2020-03-25
 */
@Transactional
@Service(interfaceClass = CodeOrderService.class, version = "1.0.0")
public class CodeOrderServiceImpl extends ServiceImpl<CodeOrderMapper, CodeOrder> implements CodeOrderService {

    private final MyLog logger = MyLog.getLog(CodeOrderServiceImpl.class);

    @Value("${qr.preview-url}")
    private String qrPreviewUrl;

    @Autowired
    private RedisUtil redisUtil;
    @Autowired
    private CodeUserService codeUserService;
    @Autowired
    private CodeInfoService codeInfoService;
    @Autowired
    private CodeAccountHistoryService codeAccountHistoryService;
    @Autowired
    private ISysConfigService sysConfigService;
    @Autowired
    private IPayOrderService payOrderService;
    @Autowired
    private IMchAccountService mchAccountService;

    @Override
    public IPage<CodeOrder> list(int currentPage, int pageSize, CodeOrder order) {
        Page page = new Page(currentPage, pageSize);
        return baseMapper.list(page, order);
    }

    @Override
    public CodeOrder getByCodeOrder(CodeOrder codeOrder) {
        return baseMapper.getByCodeOrder(codeOrder);
    }

    @Override
    public CodeOrder getByOrderNo(String orderNo) {
        return baseMapper.getByOrderNo(orderNo);
    }

    @Override
    public void confirm(CodeOrder codeOrder) {
        codeOrder = baseMapper.getByCodeOrder(codeOrder);
        // 如果订单已经确认完成,则不能进行重复确认
        if (ORDER_STATUS_SUCCESS.equals(codeOrder.getStatus()) || ORDER_STATUS_COMPLETE.equals(codeOrder.getStatus())) {
            throw new ServiceException(CODE_ORDER_ALREADY_CONFIRM);
        }
        // 加锁防止重复点击
        boolean lockSuc = redisUtil.lock(ORDER_LOCK + codeOrder.getId(), ORDER_CONFIRM_LOCK_TIME);
        if (!lockSuc) {
            throw new ServiceException(CODE_ORDER_CONFIRMING);
        }
        // 计算并更新会员\代理\码商资金,记录流水
        calcAndUpdateProfit(codeOrder);

        codeOrder.setStatus(ORDER_STATUS_SUCCESS);
        codeOrder.setConfirmTime(LocalDateTime.now());
        codeOrder.setUpdateTime(LocalDateTime.now());
        boolean isOk = super.updateById(codeOrder);
        if (!isOk) {
            throw new ServiceException(CODE_ORDER_CONFIRM_ERROR);
        }
        // 确认成功,删除锁及缓存信息
        redisUtil.delete(ORDER_LOCK + codeOrder.getId());
        redisUtil.delete(QR_CODE + codeOrder.getCodeId());
        redisUtil.delete(CODE_USER + codeOrder.getUserId());
        redisUtil.delete(ORDER_PAY + codeOrder.getOrderNo());
    }

    public void updateAmount(long id, BigDecimal newAmount) {
        CodeOrder codeOrder = super.getById(id);
        if (ORDER_STATUS_SUCCESS.equals(codeOrder.getStatus()) || ORDER_STATUS_COMPLETE.equals(codeOrder.getStatus())) {
            throw new ServiceException(CODE_ORDER_ALREADY_CONFIRM);
        }
        // 加锁防止重复点击
        boolean lockSuc = redisUtil.lock(ORDER_LOCK + codeOrder.getId(), ORDER_CONFIRM_LOCK_TIME);
        if (!lockSuc) {
            throw new ServiceException(CODE_ORDER_CONFIRMING);
        }
        CodeOrder updateOrder = new CodeOrder();
        updateOrder.setId(id);
        updateOrder.setStatus(ORDER_STATUS_SUCCESS);
        updateOrder.setOldAmount(codeOrder.getAmount());
        updateOrder.setAmount(newAmount);
        boolean isOk = super.updateById(updateOrder);

        // 计算并更新会员\代理\码商资金,记录流水
        codeOrder.setAmount(newAmount);
        calcAndUpdateProfit(codeOrder);

        if (!isOk) {
            throw new ServiceException(CODE_ORDER_CONFIRM_ERROR);
        }
        // 增加商户资金并记录流水
        PayOrder payOrder = payOrderService.findByPayOrderId(codeOrder.getOrderNo());
        long newAmountL = newAmount.multiply(new BigDecimal(100)).longValue();
        payOrder.setAmount(newAmountL);
        // 重新计算订单:渠道成本费用,代理商费用,商户入账,平台利润
        OrderCostFeeVO orderCostFeeVO = PayUtil.calOrderCostFeeAndIncome(newAmountL, payOrder.getChannelRate(), payOrder.getAgentRate(), payOrder.getParentAgentRate(), payOrder.getMchRate());
        // 重新设置渠道成本及分润
        payOrder.setChannelCost(orderCostFeeVO.getChannelCostFee());
        payOrder.setPlatProfit(orderCostFeeVO.getPlatProfit());
        payOrder.setAgentProfit(orderCostFeeVO.getAgentProfit());
        payOrder.setParentAgentProfit(orderCostFeeVO.getParentAgentProfit());
        payOrder.setMchIncome(orderCostFeeVO.getMchIncome());
        mchAccountService.changeBalanceAndInsertHistory(payOrder, false);
        // 确认成功,删除锁及缓存信息
        redisUtil.delete(ORDER_LOCK + codeOrder.getId());
        redisUtil.delete(QR_CODE + codeOrder.getCodeId());
        redisUtil.delete(CODE_USER + codeOrder.getUserId());
        redisUtil.delete(ORDER_PAY + codeOrder.getOrderNo());
    }

    /**
     * 计算并更新会员\代理\码商资金,记录流水
     *
     * @param order
     */
    private BigDecimal calcAndUpdateProfit(CodeOrder order) {
        logger.info("计算订单收益: orderNo={}, mchOrderNo={}, amount={}", order.getOrderNo(), order.getMchOrderNo(), order.getAmount());
        BigDecimal amount = order.getAmount();
        Long userId = order.getUserId();
        // 计算二维码各各级用户利润费率及利润金额
        CodeUser user = codeUserService.getById(userId);
        BigDecimal codeProfit = null;
        if (CODE_CODE_TYPE.equals(user.getType()) || CODE_CODE_SUB_TYPE.equals(user.getType())) {
            // 码商的二维码
            order.setCodeBusRate(user.getRate());
            codeProfit = amount.multiply(order.getCodeBusRate()).setScale(2, ROUND_DOWN);
            order.setCodeBusProfit(codeProfit);
        } else if (CODE_AGENT_TYPE.equals(user.getType())) {
            // 代理的二维码
            order.setAgentRate(user.getRate());
            codeProfit = amount.multiply(order.getAgentRate()).setScale(2, ROUND_DOWN);
            order.setAgentProfit(codeProfit);
            order.setCodeBusRate(user.getCodeBusRate().subtract(user.getRate()));
            order.setCodeBusProfit(amount.multiply(order.getCodeBusRate()).setScale(2, ROUND_DOWN));
        } else if (CODE_USER_TYPE.equals(user.getType())) {
            // 会员的二维码
            order.setUserRate(user.getRate());
            codeProfit = amount.multiply(order.getUserRate()).setScale(2, ROUND_DOWN);
            order.setUserProfit(codeProfit);
            // 上级为代理码商
            if (!user.getAgentId().equals(0)) {
                order.setAgentRate(user.getAgentRate().subtract(user.getRate()));
                order.setAgentProfit(amount.multiply(order.getAgentRate()).setScale(2, ROUND_DOWN));
                order.setCodeBusRate(user.getCodeBusRate().subtract(user.getAgentRate()));
            } else {
                // 上级为码商
                order.setCodeBusRate(user.getCodeBusRate().subtract(user.getRate()));
            }
            order.setCodeBusProfit(amount.multiply(order.getCodeBusRate()).setScale(2, ROUND_DOWN));
        }

        BigDecimal codeBusRate = ZERO;
        BigDecimal codeBusProfit = ZERO;
        // 更新码商资金,记录流水
        if (order.getCodeBusRate() != null && order.getCodeBusRate().compareTo(ZERO) == 1) {
            codeBusRate = user.getCodeBusRate();
            if (CODE_CODE_TYPE.equals(user.getType())) {
                codeBusRate = user.getRate();
            }
            codeBusProfit = amount.multiply(codeBusRate);
            codeUserService.addCollAmount(user.getCodeBusId(), amount, codeBusProfit);
            codeAccountHistoryService.saveCollHistory(user.getCodeBusId(), order.getOrderNo(), amount, codeBusRate, codeBusProfit, null, null, null, null);
            logger.info("记录码商收益:  codeBusId={}, rate={}, profit={}", user.getCodeBusId(), codeBusRate, codeBusProfit);
        }

        BigDecimal agentRate = ZERO;
        BigDecimal agentProfit = ZERO;
        // 更新代理资金,记录流水
        if (order.getAgentRate() != null && order.getAgentRate().compareTo(ZERO) == 1) {
            agentRate = user.getAgentRate();
            if (CODE_AGENT_TYPE.equals(user.getType())) {
                agentRate = user.getRate();
            }
            agentProfit = amount.multiply(agentRate);
            codeUserService.addCollAmount(user.getAgentId(), amount, agentProfit);
            codeAccountHistoryService.saveCollHistory(user.getAgentId(), order.getOrderNo(), amount, agentRate, agentProfit, null, null, codeBusRate, codeBusProfit);
            logger.info("记录代理收益:  agentId={}, rate={}, profit={}", user.getAgentId(), agentRate, agentProfit);
        }

        // 更新用户资金,记录流水
        if (order.getUserRate() != null && order.getUserRate().compareTo(ZERO) == 1) {
            BigDecimal userProfit = amount.multiply(user.getRate());
            codeUserService.addCollAmount(userId, amount, userProfit);
            codeAccountHistoryService.saveCollHistory(user.getId(), order.getOrderNo(), amount, user.getRate(), userProfit, agentRate, agentProfit, codeBusRate, codeBusProfit);
            logger.info("记录会员收益:  userId={}, rate={}, profit={}", userId, user.getRate(), userProfit);
        }

        // 更新二维码信息
        codeInfoService.updateCodeCollInfo(order.getCodeId(), codeProfit, order.getStatus(), order.getAmount());
        // int i = 1 / 0;
        return codeProfit;
    }

    /**
     * 分配二维码并生成订单
     *
     * @return
     */
    @Transactional
    public JSONObject distributeCodeOrder(PayOrder payOrder, Long codeBusId, int codeType, boolean needThirdId) {
        BigDecimal amount = BigDecimal.valueOf(payOrder.getAmount()).divide(new BigDecimal(100));
        logger.info("分配二维码订单开始: mchId={}, orderNo={}, mchOrderNo={}, amount={}, codeBusId={}", payOrder.getMchId(), payOrder.getPayOrderId(), payOrder.getMchOrderNo(), amount, codeBusId);
        String availableLimit = sysConfigService.getSysValue(TYPE_CODE, QR_AVAILABLE_LIMIT);
        Integer limit = Optional.ofNullable(availableLimit).map(Integer::parseInt).orElse(10);
        // 订单金额超过3000优先使用优质码
        List<CodeInfo> availableQrCode;
        if (amount.compareTo(new BigDecimal(3000)) > -1) {
            availableQrCode = codeInfoService.getAvailableBestQrCode(codeBusId, codeType, amount, needThirdId, 0, limit);
            if (CollectionUtils.isEmpty(availableQrCode)) {
                availableQrCode = codeInfoService.getAvailableQrCode(codeBusId, codeType, amount, needThirdId, 0, limit);
            }
        } else {
            availableQrCode = codeInfoService.getAvailableQrCode(codeBusId, codeType, amount, needThirdId, 0, limit);
        }
        if (CollectionUtils.isEmpty(availableQrCode)) {
            return PayUtil.makeRetFail(QRCODE_NOT_FIND);
        }
        List<Pair> pairList = availableQrCode.stream().map(e -> new Pair(e.getId(), e.getWeight())).collect(Collectors.toList());
        Map<Long, CodeInfo> qrCodeMap = availableQrCode.stream().collect(Collectors.toMap(CodeInfo::getId, t -> t));
        // 打乱顺序
        Collections.shuffle(pairList);
        Long codeId = recursiveGetCode(pairList, amount, qrCodeMap);
        if (codeId == null) {
            return PayUtil.makeRetFail(QRCODE_DISTRIBUTE_ERROR);
        }
        CodeInfo code = codeInfoService.getById(codeId);
        // 生成二维码订单信息
        CodeOrder codeOrder = new CodeOrder();
        codeOrder.setCodeId(codeId);
        codeOrder.setUserId(code.getUserId());
        codeOrder.setCodeBusId(codeBusId);
        codeOrder.setAgentId(code.getAgentId());
        codeOrder.setUsername(code.getUsername());
        codeOrder.setCodeType(code.getCodeType());
        codeOrder.setCodeName(code.getCollName());
        if (CODE_TYPE_JUHE.equals(code.getCodeType())) {
            codeOrder.setCollThirdId(code.getCollThirdId());
        } else {
            codeOrder.setCollThirdId(code.getCollThirdAccount());
        }
        codeOrder.setAmount(amount);
        codeOrder.setStatus(ORDER_STATUS_INIT);
        codeOrder.setProductId(payOrder.getProductId());
        codeOrder.setProductName(payOrder.getProductName());
        codeOrder.setOrderNo(payOrder.getPayOrderId());
        codeOrder.setMchId(payOrder.getMchId());
        codeOrder.setMchName(payOrder.getMchName());
        codeOrder.setMchOrderNo(payOrder.getMchOrderNo());
        codeOrder.setOperatePhase(OPERATE_PHASE_INIT);
        codeOrder.setScan(STATUS_NO);
        codeOrder.setCreateId(0L);
        codeOrder.setCreateTime(LocalDateTime.now());
        baseMapper.insert(codeOrder);
        JSONObject retMap = new JSONObject();
        retMap.put(PayConstant.RETURN_PARAM_RETCODE, RETURN_VALUE_SUCCESS);
        retMap.put("orderNo", payOrder.getPayOrderId());
        retMap.put("mchOrderNo", codeOrder.getMchOrderNo());
        retMap.put("amount", codeOrder.getAmount());

        String expireStr = sysConfigService.getSysValue(TYPE_CODE, ORDER_EXPIRE);
        long orderExpire = Optional.ofNullable(expireStr).map(Long::parseLong).orElse(5L);
        retMap.put("createTime", DATETIME_FORMATTER.format(LocalDateTime.now().plusMinutes(orderExpire)));
        retMap.put("codeId", codeId);
        retMap.put("codeType", code.getCodeType());
        retMap.put("codePath", qrPreviewUrl + code.getCodePath());
        retMap.put("codeUrl", code.getCodeUrl());
        retMap.put("collThirdName", code.getCollThirdName());
        retMap.put("collThirdAccount", code.getCollThirdAccount());
        retMap.put("collThirdId", code.getCollThirdId());
        logger.info("分配二维码订单返回: {}", retMap.toJSONString());
        return retMap;
    }

    /**
     * 根据权重,递归取得可用二维码
     *
     * @return
     */
    private Long recursiveGetCode(List<Pair> pairList, BigDecimal orderAmount, Map<Long, CodeInfo> qrCodeMap) {
        if (CollectionUtils.isEmpty(pairList)) {
            return null;
        }
        WeightRandom<Long, Integer> weightRandom = new WeightRandom(pairList);
        Long pid = weightRandom.random();
        logger.info("轮询取得可用二维码开始. orderAmount={}, codeId={}", orderAmount, pid);
        CodeInfo codeInfo = qrCodeMap.get(pid);
        // 当待分配列表中数据大于1时，判断分配的二维码上次订单金额是否是本次相同，如相同则继续递归获取二维码，直到最后一个二维数据不判断
        if (pairList.size() > 1 && orderAmount.compareTo(codeInfo.getLastOrderAmount()) == 0) {
            logger.info("本次订单金额与上次订单金额相同触发风控. codeId={}, lastOrderAmount={}", pid, codeInfo.getLastOrderAmount());
            // 如果最后订单金额和本次订单金额相同,则触发风控从集合中删除,然后递归取
            final CopyOnWriteArrayList<Pair> cpList = new CopyOnWriteArrayList<>(pairList);
            for (Pair pair : cpList) {
                if (codeInfo.getId().equals(pair.getKey())) {
                    cpList.remove(pair);
                }
            }
            return recursiveGetCode(cpList, orderAmount, qrCodeMap);
        }
        Map<String, Object> dayTotalMap = baseMapper.countTodayCollAmountByCodeId(pid);
        BigDecimal surplusAmount = ZERO;
        if (MapUtils.isNotEmpty(dayTotalMap)) {
            BigDecimal dayTotalAmount = new BigDecimal(dayTotalMap.getOrDefault("totalAmount", 0).toString());
            surplusAmount = codeInfo.getDayMaxAmount().subtract(dayTotalAmount).subtract(orderAmount);
            if (pairList.size() > 1 && surplusAmount.compareTo(ZERO) == -1) {
                pid = null;
                logger.info("每日收款总额触发风控. codeId={}, codeBusId={}, maxDayAmount={}, dayTotalAmount={}", pid, codeInfo.getCodeBusId(), codeInfo.getDayMaxAmount(), dayTotalAmount);
            }
            int dayTotalNum = Integer.parseInt(dayTotalMap.getOrDefault("totalNum", 0).toString());
            if (pid != null && dayTotalNum >= codeInfo.getDayMaxNum()) {
                pid = null;
                logger.info("每日交易次数触发风控. codeId={}, codeBusId={}, maxDayAmount={}, dayTotalNum={}", pid, codeInfo.getCodeBusId(), codeInfo.getDayMaxAmount(), dayTotalNum);
            }
        }
        Long codeBusId = codeInfo.getCodeBusId();
        if (pid != null && codeBusId != null && !codeBusId.equals(0L)) {
            CodeUser codeBus = codeUserService.getById(codeBusId);
            surplusAmount = codeBus.getDeposit().subtract(codeBus.getCanSettAmount()).subtract(orderAmount);
            if (surplusAmount.compareTo(ZERO) == -1) {
                pid = null;
                logger.info("码商保证金触发风控. codeId={}, codeBusId={}, realName={}, deposit={}, canSettAmount={}", pid, codeBus.getId(), codeBus.getRealName(), codeBus.getDeposit(), codeBus.getCanSettAmount());
            }
        }
        // Long agentId = codeInfo.getAgentId();
        // if (pid != null && agentId != null && !agentId.equals(0L)) {
        //     CodeUser agent = codeUserService.getById(agentId);
        //     BigDecimal totalWithdrawal = agent.getCanSettAmount().add(agent.getFrozenAmount());
        //     if (orderAmount.add(totalWithdrawal).compareTo(agent.getDeposit()) == 1) {
        //         pid = null;
        //         logger.info("代理保证金触发风控. codeId={}, agentId={}, realName={}, deposit={}, canSettAmount={}", pid, agent.getId(), agent.getRealName(), agent.getDeposit(), agent.getCanSettAmount());
        //     }
        // }
        // CodeUser codeUser = codeUserService.getById(codeInfo.getUserId());
        // if (orderAmount.add(codeUser.getCanSettAmount()).add(codeUser.getFrozenAmount()).compareTo(codeUser.getDeposit()) == 1) {
        //     pid = null;
        //     logger.info("会员保证金触发风控. codeId={}, userId={}, realName={}, deposit={}, canSettAmount={}", pid, codeUser.getId(), codeUser.getRealName(), codeUser.getDeposit(), codeUser.getCanSettAmount());
        // }
        if (pid != null) {
            // 更新二维码最后订单时间,防止同一时间段一码分配多订单
            long orderExpire = 5;
            String sysValue = sysConfigService.getSysValue(TYPE_CODE, ORDER_EXPIRE);
            if (StringUtils.isNotBlank(sysValue)) {
                orderExpire = Long.parseLong(sysValue);
            }
            // 修改二维码最后订单时间,间隔时间内不再读取此二维码分配订单,订单确认成功后恢复
            CodeInfo updateCode = new CodeInfo();
            updateCode.setId(pid);
            updateCode.setLastOrderAmount(orderAmount);
            updateCode.setLastOrderTime(LocalDateTime.now().plusMinutes(orderExpire));
            codeInfoService.updateById(updateCode);
            logger.info("轮询取得可用二维码成功. codeId={}, codeBusId={}, collName={}, weight={}, codePath={}, ", pid, codeInfo.getCodeBusId(), codeInfo.getCollName(), codeInfo.getWeight(), codeInfo.getCodePath());
            return pid;
        } else {
            // 修改二维码最后订单时间,当天不再读取此二维码分配订单
            CodeInfo updateCode = new CodeInfo();
            updateCode.setId(codeInfo.getId());
            if (surplusAmount.compareTo(new BigDecimal(1000)) < 1) {
                updateCode.setLastOrderTime(LocalDateTime.now().plusMinutes(10));
            } else {
                updateCode.setLastOrderTime(LocalDateTime.now().plusDays(1).withHour(0).withMinute(0).withSecond(0));
            }
            codeInfoService.updateById(updateCode);
        }
        // 如果触发风控,则从集合中删除,然后递归取
        final CopyOnWriteArrayList<Pair> cowList = new CopyOnWriteArrayList<>(pairList);
        for (Pair pair : cowList) {
            if (codeInfo.getId().equals(pair.getKey())) {
                cowList.remove(pair);
            }
        }
        return recursiveGetCode(cowList, orderAmount, qrCodeMap);
    }

    public boolean updateOperatePhase(String orderNo, Integer operatePhase, String clientIp, String device) {
        Integer status = null;
        if (operatePhase >= 2) {
            status = ORDER_STATUS_PAYING;
        }
        LambdaUpdateWrapper<CodeOrder> updateWrapper = new LambdaUpdateWrapper();
        updateWrapper.eq(CodeOrder::getOrderNo, orderNo).lt(CodeOrder::getOperatePhase, operatePhase).set(CodeOrder::getOperatePhase, operatePhase)
                .set(clientIp != null, CodeOrder::getClientIp, clientIp).set(CodeOrder::getUpdateTime, LocalDateTime.now());
        if (status != null) {
            updateWrapper.lt(CodeOrder::getStatus, ORDER_STATUS_SUCCESS).set(CodeOrder::getStatus, status);
        }
        Integer osType = OTHER;
        if (StringUtils.isNotBlank(device)) {
            if (device.startsWith("iOS")) {
                osType = IOS;
            } else if (device.startsWith("AndroidOS")) {
                osType = ANDROID;
            }
            updateWrapper.set(CodeOrder::getDevice, device).set(CodeOrder::getOsType, osType);
        }
        return super.update(updateWrapper);
    }

    public boolean updateStatus4Complete(String orderNo) {
        LambdaUpdateWrapper<CodeOrder> wrapper = new LambdaUpdateWrapper();
        wrapper.eq(CodeOrder::getOrderNo, orderNo);
        wrapper.set(CodeOrder::getStatus, ORDER_STATUS_COMPLETE);
        return super.update(wrapper);
    }

    public CodeOrder queryByOrderNo(String orderNo) {
        LambdaQueryWrapper<CodeOrder> wrapper = new LambdaQueryWrapper();
        wrapper.select(CodeOrder::getOrderNo, CodeOrder::getStatus);
        wrapper.eq(CodeOrder::getOrderNo, orderNo).isNull(CodeOrder::getOldAmount);
        return super.getOne(wrapper);
    }

    public List<CodeOrder> getRiskOrderList(Long codeBusId) {
        String value = sysConfigService.getSysValue(TYPE_CODE, ORDER_RISK_THRESHOLD);
        List<CodeOrder> riskOrders = baseMapper.getRiskOrder(Integer.parseInt(value), codeBusId);
        Map<String, List<CodeOrder>> collectMap = riskOrders.stream().collect(Collectors.groupingBy(CodeOrder::getClientIp));
        for (CodeOrder order : riskOrders) {
            order.setIpNum(collectMap.get(order.getClientIp()).size());
        }
        return riskOrders;
    }

    /**
     * 统计所有订单
     *
     * @return
     */
    public Map countAll(CodeOrder order) {
        return baseMapper.countAll(order);
    }

    /**
     * 统计成功订单
     *
     * @return
     */
    public Map countSuccess(CodeOrder order) {
        return baseMapper.countSuccess(order);
    }

    /**
     * 统计未付订单
     *
     * @return
     */
    public Map countFail(CodeOrder order) {
        return baseMapper.countFail(order);
    }

    public Map<Long, UserDayProfitVO> countSuccessByUser(Long codeBusId, LocalDate date) {
        List<UserDayProfitVO> profitList = baseMapper.countSuccessByUser(codeBusId, date);
        return profitList.stream().collect(Collectors.toMap(UserDayProfitVO::getUserId, Function.identity()));
    }

    /**
     * 处理失效订单
     */
    public List<CodeOrder> handleExpireOrder(LocalDateTime startTime, LocalDateTime endTime) {
        LambdaQueryWrapper<CodeOrder> wrapper = new LambdaQueryWrapper();
        wrapper.in(CodeOrder::getStatus, ORDER_STATUS_INIT, ORDER_STATUS_PAYING).between(CodeOrder::getCreateTime, startTime, endTime)
                .select(CodeOrder::getId, CodeOrder::getCodeId);
        List<CodeOrder> codeOrders = baseMapper.selectList(wrapper);
        if (CollectionUtils.isEmpty(codeOrders)) {
            return new ArrayList<>();
        }
        LambdaUpdateWrapper<CodeOrder> updateWrapper = new LambdaUpdateWrapper();
        updateWrapper.in(CodeOrder::getStatus, ORDER_STATUS_INIT, ORDER_STATUS_PAYING).between(CodeOrder::getCreateTime, startTime, endTime)
                .set(CodeOrder::getStatus, ORDER_STATUS_EXPIRED).set(CodeOrder::getUpdateTime, LocalDateTime.now());
        super.update(updateWrapper);

        String sysValue = sysConfigService.getSysValue(SYS_CONFIG_CODE, FAIL_WEIGHT);
        List<Long> codeIdList = codeOrders.stream().map(CodeOrder::getCodeId).collect(Collectors.toList());
        codeInfoService.updateFailWeight(codeIdList, Integer.valueOf(sysValue));

        // 将权重值小于等于0的二维码改为停用
        LambdaUpdateWrapper<CodeInfo> codeUpdateWrapper = new LambdaUpdateWrapper();
        codeUpdateWrapper.in(CodeInfo::getId, codeIdList).le(CodeInfo::getWeight, 0)
                .set(CodeInfo::getStatus, CODE_STATUS_STOP);
        codeInfoService.update(codeUpdateWrapper);
        return codeOrders;

    }
}
